﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace LpbPrj.Client.Controls
{
    [TemplatePart(Name = ElementButtonConfirm, Type = typeof(Button))]
    [TemplatePart(Name = ElementYearList, Type = typeof(ListBox))]
    [TemplatePart(Name = ElementMonthList, Type = typeof(ListBox))]
    [TemplatePart(Name = ElementDayList, Type = typeof(ListBox))]
    public class ListDatePicker : Control
    {
        protected Button ButtonConfirm;

        protected bool AppliedTemplate;

        public event Action Confirmed;

        public static readonly DependencyProperty SelectedTimeProperty = DependencyProperty.Register("SelectedTime", typeof(DateTime?), typeof(ListDatePicker),
            new FrameworkPropertyMetadata(default(DateTime?), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedTimeChanged));

        private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var ctl = (ListDatePicker)d;
            var v = (DateTime?)e.NewValue;
            ctl.DisplayTime = v ?? DateTime.Now;
        }

        public DateTime? SelectedTime
        {
            get => (DateTime?)GetValue(SelectedTimeProperty);
            set => SetValue(SelectedTimeProperty, value);
        }

        public static readonly DependencyProperty DisplayTimeProperty = DependencyProperty.Register("DisplayTime", typeof(DateTime), typeof(ListDatePicker),
            new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayTimeChanged));

        private static void OnDisplayTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var ctl = (ListDatePicker)d;
            var v = (DateTime)e.NewValue;
            ctl.Update(v);
        }

        public DateTime DisplayTime
        {
            get => (DateTime)GetValue(DisplayTimeProperty);
            set => SetValue(DisplayTimeProperty, value);
        }

        internal static readonly DependencyProperty ShowConfirmButtonProperty = DependencyProperty.Register(
            "ShowConfirmButton", typeof(bool), typeof(ListDatePicker), new PropertyMetadata(false));

        internal bool ShowConfirmButton
        {
            get => (bool)GetValue(ShowConfirmButtonProperty);
            set => SetValue(ShowConfirmButtonProperty, value);
        }

        protected void ButtonConfirm_OnClick(object sender, RoutedEventArgs e)
        {
            SelectedTime = DisplayTime;
            Confirmed?.Invoke();
        }


        #region Constants

        private const string ElementButtonConfirm = "PART_ButtonConfirm";
        private const string ElementYearList = "PART_YearList";
        private const string ElementMonthList = "PART_MonthList";
        private const string ElementDayList = "PART_DayList";

        #endregion Constants

        #region Data

        private ListBox _yearList;

        private ListBox _monthList;

        private ListBox _dayList;

        #endregion Data

        public override void OnApplyTemplate()
        {
            AppliedTemplate = false;
            if (ButtonConfirm != null)
            {
                ButtonConfirm.Click -= ButtonConfirm_OnClick;
            }

            if (_yearList != null)
            {
                _yearList.SelectionChanged -= HourList_SelectionChanged;
            }

            if (_monthList != null)
            {
                _monthList.SelectionChanged -= MinuteList_SelectionChanged;
            }

            if (_dayList != null)
            {
                _dayList.SelectionChanged -= SecondList_SelectionChanged;
            }

            base.OnApplyTemplate();

            _yearList = GetTemplateChild(ElementYearList) as ListBox;
            if (_yearList != null)
            {
                CreateItemsSource(_yearList, 1900, DateTime.Now.Year);
                _yearList.SelectedItem = DisplayTime.Year.ToString();
                _yearList.SelectionChanged += HourList_SelectionChanged;
            }

            _monthList = GetTemplateChild(ElementMonthList) as ListBox;
            if (_monthList != null)
            {
                CreateItemsSource(_monthList, 12);
                _monthList.SelectedItem = DisplayTime.Month.ToString("#00");
                _monthList.SelectionChanged += MinuteList_SelectionChanged;
            }

            _dayList = GetTemplateChild(ElementDayList) as ListBox;
            if (_dayList != null)
            {
                int days = DateTime.DaysInMonth(Convert.ToInt32(_yearList.SelectedItem), Convert.ToInt32(_monthList.SelectedItem));
                CreateItemsSource(_dayList, days);
                _dayList.SelectedItem = DisplayTime.Day.ToString("#00");
                _dayList.SelectionChanged += SecondList_SelectionChanged;
            }

            ButtonConfirm = GetTemplateChild(ElementButtonConfirm) as Button;
            if (ButtonConfirm != null)
            {
                ButtonConfirm.Click += ButtonConfirm_OnClick;
            }

            AppliedTemplate = true;
            if (SelectedTime.HasValue)
            {
                Update(SelectedTime.Value);
            }
            else
            {
                DisplayTime = DateTime.Now;
                Update(DisplayTime);
            }
        }

        private void HourList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            int days = DateTime.DaysInMonth(Convert.ToInt32(_yearList.SelectedItem), Convert.ToInt32(_monthList.SelectedItem));
            if (days != _dayList.Items.Count)
            {
                _dayList.SelectionChanged -= SecondList_SelectionChanged;
                CreateItemsSource(_dayList, days);
                _dayList.SelectedItem = 1.ToString("#00");
                _dayList.SelectionChanged += SecondList_SelectionChanged;
            }
            
            Update(true, false, false);
        }

        private void MinuteList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            int days = DateTime.DaysInMonth(Convert.ToInt32(_yearList.SelectedItem), Convert.ToInt32(_monthList.SelectedItem));
            if (days != _dayList.Items.Count)
            {
                _dayList.SelectionChanged -= SecondList_SelectionChanged;
                CreateItemsSource(_dayList, days);
                _dayList.SelectedItem = 1.ToString("#00");
                _dayList.SelectionChanged += SecondList_SelectionChanged;
            }

            Update(false, true, false);
        }

        private void SecondList_SelectionChanged(object sender, SelectionChangedEventArgs e) => Update(true, false, true);

        private void CreateItemsSource(ItemsControl selector, int min, int max)
        {
            var list = new List<string>();
            for (var i = min; i <= max; i++)
            {
                list.Add(i.ToString());
            }

            selector.ItemsSource = list;
        }

        private void CreateItemsSource(ItemsControl selector, int count)
        {
            var list = new List<string>();
            for (var i = 1; i <= count; i++)
            {
                list.Add(i.ToString("#00"));
            }

            selector.ItemsSource = list;
        }

        private void Update(bool year, bool month, bool day)
        {
            if (_yearList.SelectedIndex >= 0 && //_yearList.SelectedIndex < _yearList.Items.Count &&
                    _monthList.SelectedIndex >= 0 && //_monthList.SelectedIndex < _monthList.Items.Count &&
                    _dayList.SelectedIndex >= 0 //&& _dayList.SelectedIndex < _dayList.Items.Count
                    )
            {
                DisplayTime = new DateTime(Convert.ToInt32(_yearList.SelectedItem), Convert.ToInt32(_monthList.SelectedItem), Convert.ToInt32(_dayList.SelectedItem));
            }
        }

        /// <summary>
        ///     更新
        /// </summary>
        /// <param name="time"></param>
        internal void Update(DateTime time)
        {
            if (!AppliedTemplate) return;

            var h = time.Year;
            var m = time.Month;
            var s = time.Day;

            _yearList.SelectedItem = h.ToString();
            _monthList.SelectedItem = m.ToString("#00");
            _dayList.SelectedItem = s.ToString("#00");

            _yearList.ScrollIntoView(_yearList.SelectedItem);
            _monthList.ScrollIntoView(_monthList.SelectedItem);
            _dayList.ScrollIntoView(_dayList.SelectedItem);

            DisplayTime = time;
        }
    }
}